use std::borrow::Borrow;
use std::cell::RefCell;
use std::collections::HashMap;
use std::str::FromStr;
use std::sync::{Arc, RwLock};
use std::thread;
use std::thread::JoinHandle;
use std::time::SystemTime;
use crossbeam_channel::Receiver;
use rustracing::carrier::ExtractFromHttpHeader;
use rustracing::Error;
use rustracing::sampler::AllSampler;
use rustracing::tag::{Tag, TagValue};
use rustracing_jaeger::{Span, Tracer};
use rustracing_jaeger::reporter::{JaegerBinaryReporter, JaegerCompactReporter};
use rustracing_jaeger::span::{FinishedSpan, SpanContext, SpanContextState, StartSpanOptions};
use serde::Serialize;
use serde_json::json;
use crate::codes::http::{Request,Response};

pub trait RequestTracing {
    fn set_http_body(&self,span:&mut Span);
    fn set_http_header(&mut self,span:&mut Span);
    fn set_http_url(&self,span:&mut Span);
    fn set_http_method(&self,span:&mut Span);
    fn set_http_all(&mut self, span: &mut Span);
    fn set_http_error(&self,span:&mut Span,str:String);
    fn set_http_log<T>(&self,span: &mut Span, key:String,data:T) where T:ToString;
}

impl RequestTracing for Request {
    fn set_http_body(&self, span: &mut Span) {
        let body = self.get_body().to_vec();
        span.set_tag(|| -> Tag {
            Tag::new("http_body",String::from_utf8(body).unwrap())
        });
    }

    fn set_http_header(&mut self, span: &mut Span) {
        span.set_tag(|| -> Tag {
           let map = self.headers();
           let data = serde_json::to_string(map).unwrap();
           Tag::new("http_header",data)
        });
    }

    fn set_http_url(&self, span: &mut Span) {
        span.set_tag(|| -> Tag {
            Tag::new("http_url",self.get_url().to_string())
        });
    }

    fn set_http_method(&self, span: &mut Span) {
        span.set_tag(|| -> Tag {
            Tag::new("http_method",self.get_method().to_string())
        });
    }

    fn set_http_all(&mut self, span: &mut Span) {
        self.set_http_method(span);
        self.set_http_url(span);
        self.set_http_header(span);
        self.set_http_body(span);
    }

    fn set_http_error(&self, span: &mut Span,str:String) {
        span.set_tag(|| -> Tag {
            Tag::new("http_error",str)
        });
    }

    fn set_http_log<T>(&self, span: &mut Span, key:String,data: T) where T: ToString {
        span.set_tag(|| -> Tag {
            Tag::new(key,data.to_string())
        });
    }
}


pub trait ResponseTracing {
    fn into_span(&self,span:&mut Span);
}

impl ResponseTracing for Response {
    fn into_span(&self, span: &mut Span) {
        let result = self.to_string();
        span.set_tag(|| -> Tag {
            Tag::new("response",result)
        });
    }
}


pub struct JaegerTracing {
    agent: Arc<JaegerCompactReporter>,
    tracer:Tracer,
}

impl Clone for JaegerTracing {
    fn clone(&self) -> Self {
        JaegerTracing{
            agent:self.agent.clone(),
            tracer:self.tracer.clone(),
        }
    }
}

impl JaegerTracing {
    pub fn new (agent_endpoint:&str,service_name:&str) -> Self {
        let mut jaeger = JaegerCompactReporter::new(service_name).unwrap();
        jaeger.set_agent_addr(agent_endpoint.parse().unwrap());
        jaeger.set_reporter_addr("0.0.0.0:0".parse().unwrap()).unwrap();

        let (span_tx, span_rx) = crossbeam_channel::bounded(10);
        let tracer = Tracer::with_sender(AllSampler, span_tx);
        let agent = Arc::new(jaeger);
        let borrow = agent.clone();

        thread::spawn(move || {
            for data in span_rx {
                borrow.report(&[data]).unwrap();
            }
        });

        JaegerTracing{
            agent,
            tracer,
        }
    }

    pub fn get_context_from_tcp_data(tcp_data:Vec<u8>) -> Option<SpanContext> {
        let mut request = Request::read_from_data(tcp_data)?;
        let s = SpanContextState::extract_from_http_header(request.headers());
        match s {
            Err(_) => None,
            Ok(data)=> {
                let span_context = data?;
                Some(span_context)
            }
        }
    }
}

impl JaegerTracing {
    pub fn start_span(&self, operate_name:String,parent:Option<&SpanContext>) -> Span {
        let tracer = self.tracer.clone();
        let mut span = tracer.span(operate_name);
        span = span.start_time(SystemTime::now());
        if let Some(sc) = parent {
            span = span.child_of(sc);
        }

        span.start()
    }


    //生命周期结束自动提交
    pub fn finish_span(&self,data:Span) {
        println!("finish span :{}",data.context().unwrap().state().trace_id().to_string())
    }
}

#[cfg(test)]
mod test {
    use std::io::Read;
    use crate::trace::jaeger::JaegerTracing;
    use rustracing::sampler::AllSampler;
    use std::sync::{Arc, RwLock};
    use rustracing_jaeger::Tracer;
    use rustracing_jaeger::reporter::{JaegerBinaryReporter, JaegerCompactReporter};
    use std::net::{TcpListener, UdpSocket};
    use std::thread;
    use rustracing::tag::Tag;

    #[test]
    fn test () {
        let mut client = JaegerTracing::new("192.168.189.128:31831","test");
        let mut span = client.start_span("test_mime".to_string(),None);

    }

    fn test_tcp() {
        let mut tcp = TcpListener::bind("0.0.0.0:8888").unwrap();
        for stream in tcp.incoming() {
            let mut s = stream.unwrap();
            thread::spawn(move || {
                loop {
                    let mut buf = [0;1024*10];
                    let size = s.read(&mut buf).unwrap();

                    let data = JaegerTracing::get_context_from_tcp_data(buf[..size].to_vec());
                    match data {
                        None=>{println!("not a trace request");}
                        Some(span_context) => {
                            println!("{:?}",span_context.state());
                        }
                    }
                }
            });
        }
    }

}